Skip to content

feat: WebSocket auth hardening and e2e tests#45

Closed
CaddyGlow wants to merge 4 commits intofeat/codex-msaf-compatibilityfrom
feat/codex-websocket-hardening
Closed

feat: WebSocket auth hardening and e2e tests#45
CaddyGlow wants to merge 4 commits intofeat/codex-msaf-compatibilityfrom
feat/codex-websocket-hardening

Conversation

@CaddyGlow
Copy link
Copy Markdown
Owner

Summary

  • Rewrote WebSocket authentication to use denial responses before accepting connections
  • Added WebSocket bypass/alias handling with mock response streaming and model alias restoration
  • Added request context tracking and payload sanitization through adapter pipeline
  • Simplified detection prompt merging to always merge with fallback data
  • Added per-plugin use_mock_adapter_in_bypass_mode control flag
  • Removed agent-framework dependency

PR Stack

This is PR 3 of 3 split from #41. Stacked on #44. Merge in order:

  1. feat: Codex v0.114.0 integration with gpt-5.4 support #43 - Core Codex v0.114.0 integration
  2. feat: Microsoft Agent Framework compatibility and bypass mode #44 - MSAF compatibility and bypass mode
  3. This PR - WebSocket auth hardening and e2e tests

Combined diff of all 3 PRs equals the original PR #41 diff exactly.

Test plan

  • All pre-commit checks pass (ruff, mypy, format)
  • 1112 unit/plugin tests pass (+11 over PR2)
  • WebSocket route tests verify auth, settings resolution, payload sanitization
  • WebSocket integration tests verify connection lifecycle and streaming
  • Detection alias merge tests verify fallback prompt merging
  • Factory bypass tests verify per-plugin mock adapter control

Copilot AI review requested due to automatic review settings March 20, 2026 14:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the Codex WebSocket authentication flow (denying unauthorized handshakes before accepting) and expands WebSocket-specific test coverage, including unit helpers and end-to-end integration validation across both v1 and legacy WS paths.

Changes:

  • Deny unauthorized WebSocket handshakes pre-accept(), and add per-connection request context + model alias restoration in WS streaming.
  • Improve bypass-mode behavior by injecting a mock handler into real adapters (per-plugin configurable), enabling Codex WS mock streaming while keeping the Codex adapter active.
  • Add substantial WebSocket unit/integration/e2e coverage plus shared WS test data + validation helpers.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
ccproxy/plugins/codex/routes.py WebSocket auth denial before accept; WS request context, model alias restoration, mock streaming support.
ccproxy/plugins/codex/adapter.py Route mock handling via mock_handler injection; keep Codex adapter active in bypass mode.
ccproxy/plugins/codex/detection_service.py Merge partial detected prompt cache with fallback prompts.
ccproxy/plugins/codex/plugin.py Opt Codex out of default bypass-mode MockAdapter swap (use_mock_adapter_in_bypass_mode = False).
ccproxy/core/plugins/factories.py Add per-plugin bypass control and inject mock_handler into adapters when bypass mode is enabled.
ccproxy/services/adapters/base.py Add mock_handler plumbing to base adapter init.
ccproxy/services/adapters/mock_adapter.py Tighten return typing via casts for streaming paths.
tests/unit/plugins/test_codex_detection.py Add unit test for prompt-cache merge behavior.
tests/unit/core/test_provider_factory_bypass.py Update bypass-mode factory expectations for Codex (real adapter + injected mock handler).
tests/plugins/codex/unit/test_routes.py New unit tests for WS helper behavior (settings resolution, payload sanitization, model alias restore).
tests/plugins/codex/integration/test_codex_websocket.py New integration tests covering WS streaming, warmup behavior, bypass mock streaming, and auth denial.
tests/integration/test_websocket_e2e.py New end-to-end WS suite + optional live-server WS tests.
tests/helpers/test_data.py Add WS endpoint configs + request builders + terminal event sets.
tests/helpers/e2e_validation.py Add WS event-sequence/content/warmup/error validation helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +238 to +271
async def _prepare_mock_websocket_payload(
adapter: "CodexAdapter",
provider_payload: dict[str, Any],
request_context: RequestContext,
) -> dict[str, Any]:
body_bytes = json.dumps(provider_payload).encode("utf-8")
body_bytes = await adapter._map_request_model(request_context, body_bytes)
payload = json.loads(body_bytes.decode("utf-8"))

if adapter._should_apply_detection_payload():
payload = adapter._apply_request_template(payload)
detected_instructions = adapter._get_instructions()
else:
payload = adapter._normalize_input_messages(payload)
detected_instructions = ""

existing_instructions = payload.get("instructions")
if isinstance(existing_instructions, str) and existing_instructions:
instructions = (
f"{detected_instructions}\n{existing_instructions}"
if detected_instructions
else existing_instructions
)
else:
instructions = detected_instructions

if instructions:
payload["instructions"] = instructions
else:
payload.pop("instructions", None)

payload["stream"] = True
payload["store"] = False
return payload
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In bypass mode the WebSocket path uses _prepare_mock_websocket_payload() instead of prepare_provider_request(), but it currently doesn’t apply the same payload sanitization as the real path (e.g., removing unsupported keys like max_tokens/temperature, filtering item_reference inputs, and stripping _-prefixed metadata fields). This makes bypass-mode WebSocket behavior diverge from normal WebSocket behavior and can leak internal metadata into mock streams. Consider reusing the same sanitization steps from CodexAdapter.prepare_provider_request() (minus auth/header work) so both paths normalize payloads consistently.

Copilot uses AI. Check for mistakes.
@CaddyGlow CaddyGlow force-pushed the feat/codex-msaf-compatibility branch from a06adec to 9c34087 Compare March 20, 2026 14:19
@CaddyGlow CaddyGlow force-pushed the feat/codex-websocket-hardening branch from 397981a to 89e3a8a Compare March 20, 2026 14:19
dabogee and others added 3 commits March 20, 2026 15:24
Add MSAF-compatible mock response handling and bypass mode infrastructure:
- Add OpenAI Responses format support to mock handler with proper SSE events
- Add prompt text extraction for context-aware mock responses
- Add format-aware mock adapter with target format detection
- Add bypass mode to plugin factory with MockAdapter fallback
- Add inject_detection_payload config toggle for generic API usage
- Add openai_thinking_xml streaming configuration
- Add format adapter streaming configuration support
- Add MSAF integration and real library tests
- Add bypass mode factory tests
Harden WebSocket authentication and add comprehensive testing:
- Rewrite WebSocket auth to use denial responses before accepting
- Add separate auth checks for missing and invalid tokens
- Add WebSocket bypass/alias handling with mock response streaming
- Add request context and model alias restoration for WebSocket events
- Add WebSocket payload sanitization through adapter pipeline
- Simplify detection prompt merging to always merge with fallback
- Add use_mock_adapter_in_bypass_mode flag for per-plugin control
- Pass mock_handler to adapter kwargs for integrated bypass handling
- Add WebSocket e2e tests, route tests, and detection alias tests
- Remove agent-framework dependency
Extract _sanitize_provider_body from prepare_provider_request so both
the real and mock WebSocket paths apply the same Codex-specific cleanup:
removing unsupported keys, filtering item_reference inputs, and
stripping _-prefixed metadata fields.
@CaddyGlow CaddyGlow force-pushed the feat/codex-msaf-compatibility branch from 9c34087 to 40f264e Compare March 20, 2026 14:25
@CaddyGlow CaddyGlow force-pushed the feat/codex-websocket-hardening branch from 169de0c to 98e8f3d Compare March 20, 2026 14:26
… errors

_make_websocket_terminal_event was always emitting type
"response.completed" even for errors, and omitting sequence_number.
Per the OpenAI Responses API spec (ResponseFailedEvent model), error
terminal events must use type "response.failed" and all stream events
require a sequence_number field.
@CaddyGlow CaddyGlow force-pushed the feat/codex-msaf-compatibility branch from f2f2215 to f387474 Compare March 20, 2026 14:49
@CaddyGlow CaddyGlow deleted the branch feat/codex-msaf-compatibility March 20, 2026 14:49
@CaddyGlow CaddyGlow closed this Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants